package com.xxl.job.core.executor.impl;

import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.glue.GlueFactory;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.reflect.Method;
import java.util.Map;


/**
 * xxl-job executor (for spring)
 *
 * @author xuxueli 2018-11-01 09:24:52
 */
public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware,
    SmartInitializingSingleton, DisposableBean {

  private static final Logger logger = LoggerFactory.getLogger(XxlJobSpringExecutor.class);


  // start
  @Override
  public void afterSingletonsInstantiated() {

    // init JobHandler Repository
    /*initJobHandlerRepository(applicationContext);*/

    // init JobHandler Repository (for method)
    initJobHandlerMethodRepository(applicationContext);

    // refresh GlueFactory
    GlueFactory.refreshInstance(1);

    // super start
    try {
      super.start();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  // destroy
  @Override
  public void destroy() {
    super.destroy();
  }


    /*private void initJobHandlerRepository(ApplicationContext applicationContext) {
        if (applicationContext == null) {
            return;
        }

        // init job handler action
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);

        if (serviceBeanMap != null && serviceBeanMap.size() > 0) {
            for (Object serviceBean : serviceBeanMap.values()) {
                if (serviceBean instanceof IJobHandler) {
                    String name = serviceBean.getClass().getAnnotation(JobHandler.class).value();
                    IJobHandler handler = (IJobHandler) serviceBean;
                    if (loadJobHandler(name) != null) {
                        throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts.");
                    }
                    registJobHandler(name, handler);
                }
            }
        }
    }*/

  private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
    if (applicationContext == null) {
      return;
    }
    // init job handler from method
    String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false,
        true);
    for (String beanDefinitionName : beanDefinitionNames) {
      Object bean = applicationContext.getBean(beanDefinitionName);

      Map<Method, XxlJob> annotatedMethods = null;   // referred to ：org.springframework.context.event.EventListenerMethodProcessor.processBean
      try {
        annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
            new MethodIntrospector.MetadataLookup<XxlJob>() {
              @Override
              public XxlJob inspect(Method method) {
                return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
              }
            });
      } catch (Throwable ex) {
        logger.error(
            "xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
      }
      if (annotatedMethods == null || annotatedMethods.isEmpty()) {
        continue;
      }

      for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
        Method executeMethod = methodXxlJobEntry.getKey();
        XxlJob xxlJob = methodXxlJobEntry.getValue();
        // regist
        registJobHandler(xxlJob, bean, executeMethod);
      }
    }
  }

  // ---------------------- applicationContext ----------------------
  private static ApplicationContext applicationContext;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    XxlJobSpringExecutor.applicationContext = applicationContext;
  }

  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }

}
